
#include <Liberty3D/Engine/Windows/Stream.h>

#include <ios>

using namespace Liberty::Engine;
using namespace System;
using namespace System::IO;
using namespace cli;

namespace Liberty { namespace Engine {

StreamBuf_To_Stream::StreamBuf_To_Stream(std::streambuf* buf) : _buf(buf) {
}

bool StreamBuf_To_Stream::CanSeek::get() {
    return true;
}

__int64 StreamBuf_To_Stream::Position::get() {
    return _buf->pubseekpos(0);
}

void StreamBuf_To_Stream::Position::set(__int64 p) {
    _buf->pubseekoff(p, std::ios::end);
}

__int64 StreamBuf_To_Stream::Length::get() {
    std::streampos pos = _buf->pubseekpos(0);
    std::streampos end = _buf->pubseekoff(0, std::ios::end);

    _buf->pubseekoff(pos, std::ios::beg);
    return end;
}

void StreamBuf_To_Stream::Flush() {
    _buf->pubsync();
}

void StreamBuf_To_Stream::SetLength(__int64 p) {
    throw gcnew System::NotSupportedException();
}

__int64 StreamBuf_To_Stream::Seek(__int64 p, SeekOrigin origin) {
    switch (origin) {
    case SeekOrigin::Begin:   return _buf->pubseekoff(p, std::ios::beg);
    case SeekOrigin::Current: return _buf->pubseekoff(p, std::ios::cur);
    case SeekOrigin::End:     return _buf->pubseekoff(p, std::ios::end);
    }

    throw gcnew System::NotSupportedException();
}

int StreamBuf_To_Stream::Read(array<unsigned char>^ buffer, int offset, int count) {
    pin_ptr<unsigned char> p1 = &buffer[0];

    return (int)_buf->sgetn(((char*)(unsigned char*)p1) + offset, count);
}

void StreamBuf_To_Stream::Write(array<unsigned char>^ buffer, int offset, int count) {
    pin_ptr<unsigned char> p1 = &buffer[0];
    char*                  p2 = ((char*)(unsigned char*)p1) + offset;

    while (count) {
        int written = (int)_buf->sputn(p2, count);

        p2    += written;
        count -= written;
    }
}

StreamBuf_To_InputStream::StreamBuf_To_InputStream(std::streambuf* buf) : StreamBuf_To_Stream(buf) {
}

bool StreamBuf_To_InputStream::CanRead::get() {
    return true;
}

bool StreamBuf_To_InputStream::CanWrite::get() {
    return false;
}

StreamBuf_To_OutputStream::StreamBuf_To_OutputStream(std::streambuf* buf) : StreamBuf_To_Stream(buf) {
}

bool StreamBuf_To_OutputStream::CanRead::get() {
    return false;
}

bool StreamBuf_To_OutputStream::CanWrite::get() {
    return true;
}

Stream^ CreateInputStream(std::streambuf* buf) {
    return gcnew StreamBuf_To_InputStream(buf);
}

Stream^ CreateOutputStream(std::streambuf* buf) {
    return gcnew StreamBuf_To_OutputStream(buf);
}

}}

